package com.blockchain.console.servlets.util;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

import com.blockchain.common.enums.CoinType;
import com.blockchain.console.service.common.entity.EthereumEntity;
import com.blockchain.framework.service.exception.ParameterException;
import com.blockchain.util.HttpRequest;
import com.blockchain.util.StringHelper;

public class EthereumUtil implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static final String unitStr = "1000000000000000000";//默认以太坊

	public static BigDecimal realValue(String input ,String unit){
		BigDecimal bigDecimal = new BigDecimal(input);
		if (StringHelper.isEmpty(unit)) {
			unit = unitStr;
		}
		bigDecimal = bigDecimal.divide(new BigDecimal(unit), 8, BigDecimal.ROUND_HALF_DOWN);
		return bigDecimal;
	}

	public static BigDecimal weiValue(String input,String unit) {
		BigDecimal bigDecimal = new BigDecimal(input);
		if (StringHelper.isEmpty(unit)) {
			unit = unitStr;
		}
		bigDecimal = bigDecimal.multiply(new BigDecimal(unit));
		return bigDecimal;
	}

	public static BigInteger decodeHex(String hex_num) throws Throwable {
		hex_num = hex_num.replace("0x", "");
		BigInteger bigInterger = new BigInteger(hex_num, 16);
		return bigInterger;
	}



	public static String encodeDec(BigDecimal num) throws Throwable {
		String strPart = StringHelper.toUnsignedString(num, 16);
		return "0x" +strPart;  
	}

	public static String fill_zero(String input) throws Throwable {
		String str = input.replace("0x", ""); 
		int strLen = input.length();
		StringBuffer sb = null;
		while (strLen < 64) {
			sb = new StringBuffer();
			sb.append("0").append(str);// 左补0
			str = sb.toString();
			strLen = str.length();
		}
		return str;
	}

	public static String getridof_zero(String input) throws Throwable {
		String str = input.replaceFirst("^0*", "");
		return "0x" + str;
	}

	public static Map<String, Object> personal_newAccount(String ip , String port, String pass) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","personal_newAccount");
		map.put("id","1");
		map.put("params", new String[]{pass});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		Map<String, Object> json = JsonUtil.GsonToMaps(result);
		return json;
	}

	public static Map<String, Object> personal_unlockAccount(String ip , String port,String address, String password) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","personal_unlockAccount");
		map.put("id","1");
		map.put("params", new Object[]{address,password,60});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		Map<String, Object> json = JsonUtil.GsonToMaps(result);
		return json;
	}

	public static Map<String, Object> personal_lockAccount(String ip , String port, String address) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","personal_lockAccount");
		map.put("id","1");
		map.put("params", new String[]{address});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		Map<String, Object> json = JsonUtil.GsonToMaps(result);
		return json;
	}

	public static EthereumEntity eth_protocolVersion(String ip , String port) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","eth_protocolVersion");
		map.put("id","1");
		map.put("params", new String[]{});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		EthereumEntity json = JsonUtil.GsonToBean(result, EthereumEntity.class);
		return json;
	}

	public static Map<String, Object> eth_accounts(String ip , String port) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","eth_accounts");
		map.put("id","1");
		map.put("params", new String[]{});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		Map<String, Object> json = JsonUtil.GsonToMaps(result);
		return json;
	}


	public static BigDecimal eth_getBalance(String ip , String port,String address) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","eth_getBalance");
		map.put("id","1");
		map.put("params", new String[]{address , "latest"});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		EthereumEntity json = JsonUtil.GsonToBean(result, EthereumEntity.class);
		if (json.error != null) {
			throw new ParameterException("代币钱包链接失败" + json.error);
		}else{
			BigInteger decodeStr = decodeHex((String)json.result);
			return realValue(decodeStr.toString(), unitStr);	
		}
	}

	public static Map<String, Object> eth_getTransactionCount(String ip , String port,String address) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","eth_getTransactionCount");
		map.put("id","1");
		map.put("params", new String[]{address , "latest"});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		Map<String, Object> json = JsonUtil.GsonToMaps(result);
		return json;
	}


	public static EthereumEntity eth_sendTransaction(String ip , String port, String address, String value, String toaddress , String data) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","eth_sendTransaction");
		map.put("id","1");

		HashMap<String, Object> parmas = new HashMap<String, Object>();
		parmas.put("from", address);
		parmas.put("value", value);
		parmas.put("to", toaddress);
		parmas.put("gasPrice", encodeDec(new BigDecimal("1000000000")));
//		parmas.put("gas", encodeDec(new BigDecimal("25201")));
		parmas.put("data", data);
		map.put("params", new Object[]{parmas});

		System.out.println(url.toString());
		System.out.println(JsonUtil.GsonString(map));
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		EthereumEntity json = JsonUtil.GsonToBean(result, EthereumEntity.class);
		return json;
	}

	public static String eth_gasPrice(String ip , String port) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","eth_gasPrice");
		map.put("id","1");
		map.put("params", new String[]{});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		Map<String, Object> json = JsonUtil.GsonToMaps(result);
		if (json.get("result") != null){
			return json.get("result").toString();
		}
		return "0";
	}

	public static String eth_blockNumber(String ip , String port)throws Throwable{
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","eth_blockNumber");
		map.put("id","1");
		map.put("params", new String[]{});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		Map<String, Object> json = JsonUtil.GsonToMaps(result);
		if (json.get("result") != null){
			return decodeHex(json.get("result").toString()).toString();
		}
		return "0";
	}

	public static Map<String, Object> eth_getTransactionByHash(String ip , String port,String hash) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","eth_getTransactionByHash");
		map.put("id","1");
		map.put("params", new String[]{hash});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		Map<String, Object> json = JsonUtil.GsonToMaps(result);
		return json;
	}

	public static BigInteger eth_getBlockTransactionCountByNumber(String ip , String port,int index) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","eth_getBlockTransactionCountByNumber");
		map.put("id","1");
		map.put("params", new String[]{encodeDec(new BigDecimal(index))});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		Map<String, Object> json = JsonUtil.GsonToMaps(result);
		if (json.get("result") != null){
			return decodeHex(json.get("result").toString());
		}
		return new BigInteger("0");
	}

	public static EthereumEntity eth_getTransactionByBlockNumberAndIndex(String ip , String port,int blockNumber, int index) throws Throwable {
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","eth_getTransactionByBlockNumberAndIndex");
		map.put("id","1");
		map.put("params", new String[]{encodeDec(new BigDecimal(blockNumber)),encodeDec(new BigDecimal(index))});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		EthereumEntity json = JsonUtil.GsonToBean(result, EthereumEntity.class);
		return json;
	}
	
	/**
	 * 获取代币余额 
	 * @param contractAddress
	 * @param address
	 * @return
	 * @throws Throwable
	 */
	public static BigDecimal contract_getBalance(String ip , String port,String contractAddress, String address, String unit) throws Throwable{
		StringBuffer url = new StringBuffer(ip).append(":" + port);
		String method  = "0x70a08231";
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("jsonrpc","2.0");
		map.put("method","eth_call");
		map.put("id","1");

		HashMap<String, Object> parmas = new HashMap<String, Object>();
		parmas.put("to", contractAddress);
		parmas.put("data", method + fill_zero(address));

		map.put("params", new Object[]{parmas, "latest"});
		String result = HttpRequest.sendPost( url.toString() , JsonUtil.GsonString(map),CoinType.ETH.getName());
		EthereumEntity json = JsonUtil.GsonToBean(result, EthereumEntity.class);
		if (json.error != null) {
			throw new ParameterException("代币钱包链接失败" + json.error);
		}else{
			return realValue(decodeHex((String)json.result).toString(), unit);	
		}
	}
}
